home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 7 / Amiga Format AFCD07 (Dec 1996, Issue 91).iso / serious / shareware / programming / emacs-complete / fsf / emacs / oldxmenu / activate.c next >
Encoding:
C/C++ Source or Header  |  1993-11-26  |  15.0 KB  |  546 lines

  1. /* $Header: /u/src/emacs/19.0/oldXMenu/RCS/Activate.c,v 1.1 1992/04/11 22:10:17 jimb Exp $ */
  2. /* Copyright    Massachusetts Institute of Technology    1985    */
  3.  
  4. #include "copyright.h"
  5.  
  6. /*
  7.  * XMenu:    MIT Project Athena, X Window system menu package
  8.  *
  9.  *    XMenuActivate -    Maps a given menu to the display and activates
  10.  *            the menu for user selection.  The user is allowed to
  11.  *            specify which pane and selection will be current,
  12.  *            the X and Y location of the menu (relative to the
  13.  *            parent window) and the mouse button event mask that
  14.  *            will be used to identify a selection request.
  15.  *
  16.  *            A menu selection is shown to be current by placing
  17.  *            a highlight box around the selection as the mouse
  18.  *            cursor enters its active region.  Inactive selections
  19.  *            will not be highlited.    As the mouse cursor moved
  20.  *            from one menu pane to another menu pane the pane being
  21.  *            entered is raised and made current and the pane being
  22.  *            left is lowered.
  23.  *
  24.  *            Anytime XMenuActivate returns, the p_num and
  25.  *            s_num are left at their last known values (i.e.,
  26.  *            the last known current pane and selection indices).
  27.  *            The following are the defined return states:
  28.  *
  29.  *            1)    If at any time an error occurs the data
  30.  *                pointer is left untouched and XM_FAILURE
  31.  *                is returned.  
  32.  *
  33.  *            2)    When a selection request is received (i.e.,
  34.  *                when the specified mouse event occurs) the
  35.  *                data pointer will be set to the data
  36.  *                associated with the particular selection
  37.  *                current at the time of the selection request
  38.  *                and XM_SUCCESS is returned.
  39.  *
  40.  *            3)    If no selection was current at the time a
  41.  *                selection request is made the data pointer
  42.  *                will be left untouched and XM_NO_SELECT will
  43.  *                be returned.
  44.  *
  45.  *            4)    If the selection that was current at the time 
  46.  *                a selection request is made is not an active
  47.  *                selection the data pointer will be left
  48.  *                untouched and XM_IA_SELECT will be returned.
  49.  *
  50.  *            Since X processes events in an asynchronous manner
  51.  *            it is likely that XMenuActivate will encounter
  52.  *            a "foreign event" while it is executing.  Foreign
  53.  *            events are handled in one of three ways:
  54.  *
  55.  *            1)    The event is discarded.  This is the default
  56.  *                mode and requires no action on the part of the
  57.  *                application.
  58.  *
  59.  *            2)    The application has identified an asynchronous
  60.  *                event handler that will be called and the
  61.  *                foreign event handed off to it.  Note:
  62.  *                AEQ mode disables this mode temporarily.
  63.  *
  64.  *            3)    The application has enabled asynchronous event
  65.  *                queueing mode.  In this mode all foreign events
  66.  *                will be    queued up untill XMenuActivate
  67.  *                terminates; at which time they will be
  68.  *                returned to the    X event queue.  As long as
  69.  *                AEQ mode is enabled any asynchronous event
  70.  *                handler as temporarily disabled.
  71.  *
  72.  *            Any events encountered while taking down the menu
  73.  *            (i.e., exposure events from occluded windows) will
  74.  *            automatically be returned to the X event queue after
  75.  *            XMenuActivate has cleaned the queue of any of its own
  76.  *            events that are no longer needed.
  77.  *
  78.  *    Author:        Tony Della Fera, DEC
  79.  *            March 12, 1986
  80.  *
  81.  */
  82.  
  83. #include "XMenuInt.h"
  84.  
  85. int
  86. XMenuActivate(display, menu, p_num, s_num, x_pos, y_pos, event_mask, data)
  87.     register Display *display;        /* Display to put menu on. */
  88.     register XMenu *menu;        /* Menu to activate. */
  89.     int *p_num;                /* Pane number selected. */
  90.     int *s_num;                /* Selection number selected. */
  91.     int x_pos;                /* X coordinate of menu position. */
  92.     int y_pos;                /* Y coordinate of menu position. */
  93.     unsigned int event_mask;        /* Mouse button event mask. */
  94.     char **data;            /* Pointer to return data value. */
  95. {
  96.     int status;                /* X routine call status. */
  97.     int orig_x;                /* Upper left menu origin X coord. */
  98.     int orig_y;                /* Upper left menu origin Y coord. */
  99.     int ret_val;            /* Return value. */
  100.  
  101.     register XMPane *p_ptr;        /* Current XMPane. */
  102.     register XMPane *event_xmp;        /* Event XMPane pointer. */
  103.     register XMPane *cur_p;        /* Current pane. */
  104.     register XMSelect *cur_s;        /* Current selection. */
  105.     XMWindow *event_xmw;        /* Event XMWindow pointer. */
  106.     XEvent event;            /* X input event. */
  107.     XEvent peek_event;            /* X input peek ahead event. */
  108.  
  109.     Bool selection = False;        /* Selection has been made. */
  110.     Bool forward = True;        /* Moving forward in the pane list. */
  111.  
  112.     Window root, child;
  113.     int root_x, root_y, win_x, win_y;
  114.     unsigned int mask;
  115.  
  116.     /*
  117.      * Define and allocate a foreign event queue to hold events
  118.      * that don't belong to XMenu.  These events are later restored
  119.      * to the X event queue.
  120.      */
  121.     typedef struct _xmeventque {
  122.     XEvent event;
  123.     struct _xmeventque *next;
  124.     } XMEventQue;
  125.  
  126.     XMEventQue *feq = NULL;            /* Foreign event queue. */
  127.     XMEventQue *feq_tmp;        /* Foreign event queue temporary. */
  128.     
  129.     /*
  130.      * If there are no panes in the menu then return failure
  131.      * because the menu is not initialized.
  132.      */
  133.     if (menu->p_count == 0) {
  134.     _XMErrorCode = XME_NOT_INIT;
  135.     return(XM_FAILURE);
  136.     }
  137.  
  138.     /*
  139.      * Find the desired current pane.
  140.      */
  141.     cur_p = _XMGetPanePtr(menu, *p_num);
  142.     if (cur_p == NULL) {
  143.     return(XM_FAILURE);
  144.     }
  145.     cur_p->activated = cur_p->active;
  146.  
  147.     /*
  148.      * Find the desired current selection.
  149.      * If the current selection index is out of range a null current selection
  150.      * will be assumed and the cursor will be placed in the current pane
  151.      * header.
  152.      */
  153.     cur_s = _XMGetSelectionPtr(cur_p, *s_num);
  154.  
  155.     /*
  156.      * Compute origin of menu so that cursor is in
  157.      * Correct pane and selection.
  158.      */
  159.     _XMTransToOrigin(display, 
  160.              menu, 
  161.              cur_p, cur_s, 
  162.              x_pos, y_pos, 
  163.              &orig_x, &orig_y);
  164.     menu->x_pos = orig_x;    /* Store X and Y coords of menu. */
  165.     menu->y_pos = orig_y;
  166.     
  167.     if (XMenuRecompute(display, menu) == XM_FAILURE) {
  168.     return(XM_FAILURE);
  169.     }
  170.  
  171.     /*
  172.      * Flush the window creation queue.
  173.      * This batches all window creates since lazy evaluation
  174.      * is more efficient than individual evaluation.
  175.      * This routine also does an XFlush().
  176.      */
  177.     if (_XMWinQueFlush(display, menu, cur_p, cur_s) == _FAILURE) {
  178.     return(XM_FAILURE);
  179.     }
  180.  
  181.     /*
  182.      * Make sure windows are in correct order (in case we were passed
  183.      * an already created menu in incorrect order.)
  184.      */
  185.     for(p_ptr = menu->p_list->next; p_ptr != cur_p; p_ptr = p_ptr->next)
  186.     XRaiseWindow(display, p_ptr->window);
  187.     for(p_ptr = menu->p_list->prev; p_ptr != cur_p->prev; p_ptr = p_ptr->prev)
  188.     XRaiseWindow(display, p_ptr->window);
  189.  
  190.     /*
  191.      * Make sure all selection windows are mapped.
  192.      */
  193.     for (
  194.     p_ptr = menu->p_list->next;
  195.     p_ptr != menu->p_list;
  196.     p_ptr = p_ptr->next
  197.     ){
  198.     XMapSubwindows(display, p_ptr->window);
  199.     }
  200.  
  201.     /*
  202.      * Synchronize the X buffers and the event queue.
  203.      * From here on, all events in the queue that don't belong to
  204.      * XMenu are sent back to the application via an application
  205.      * provided event handler or discarded if the application has
  206.      * not provided an event handler.
  207.      */
  208.     XSync(display, 0);
  209.     
  210.     /*
  211.      * Grab the mouse for menu input.
  212.      */
  213.     
  214.     status = XGrabPointer(
  215.               display,
  216.               menu->parent,
  217.               True,
  218.               event_mask,
  219.               GrabModeAsync,
  220.               GrabModeAsync,
  221.               None,
  222.               menu->mouse_cursor,
  223.               CurrentTime
  224.               );
  225.     if (status == _X_FAILURE) {
  226.     _XMErrorCode = XME_GRAB_MOUSE;
  227.     return(XM_FAILURE);
  228.     }
  229.  
  230.     /*
  231.      * Map the menu panes.
  232.      */
  233.     XMapWindow(display, cur_p->window);
  234.     for (p_ptr = menu->p_list->next;
  235.      p_ptr != cur_p; 
  236.      p_ptr = p_ptr->next)
  237.       XMapWindow(display, p_ptr->window);
  238.     for (p_ptr = cur_p->next;
  239.      p_ptr != menu->p_list;
  240.      p_ptr = p_ptr->next)
  241.       XMapWindow(display, p_ptr->window);
  242.  
  243.     XRaiseWindow(display, cur_p->window);    /* Make sure current */
  244.                         /* pane is on top. */
  245.     
  246.     cur_s = NULL;            /* Clear current selection. */
  247.  
  248.     /*
  249.      * Begin event processing loop.
  250.      */
  251.     while (1) {
  252.     XNextEvent(display, &event);    /* Get next event. */
  253.     switch (event.type) {        /* Dispatch on the event type. */
  254.     case Expose:
  255.         event_xmp = (XMPane *)XLookUpAssoc(display,
  256.                            menu->assoc_tab, 
  257.                            event.xexpose.window);
  258.         if (event_xmp == NULL) {
  259.         /*
  260.          * If AEQ mode is enabled then queue the event.
  261.          */
  262.         if (menu->aeq) {
  263.             feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
  264.             if (feq_tmp == NULL) {
  265.             _XMErrorCode = XME_CALLOC;
  266.             return(XM_FAILURE);
  267.             }
  268.             feq_tmp->event = event;
  269.             feq_tmp->next = feq;
  270.             feq = feq_tmp;
  271.         }
  272.         else if (_XMEventHandler) (*_XMEventHandler)(&event);
  273.         break;
  274.         }
  275.         if (event_xmp->activated) {
  276.         XSetWindowBackground(display,
  277.                      event_xmp->window, 
  278.                      menu->bkgnd_color);
  279.         }
  280.         else {
  281.         XSetWindowBackgroundPixmap(display,
  282.                        event_xmp->window,
  283.                        menu->inact_pixmap);
  284.         }
  285.         _XMRefreshPane(display, menu, event_xmp);
  286.         break;
  287.     case EnterNotify:
  288.         /* 
  289.          * First wait a small period of time, and see
  290.          * if another EnterNotify event follows hard on the
  291.          * heels of this one. i.e., the user is simply
  292.          * "passing through". If so, ignore this one.
  293.          */
  294.     
  295.         event_xmw = (XMWindow *)XLookUpAssoc(display,
  296.                          menu->assoc_tab,
  297.                          event.xcrossing.window);
  298.         if (event_xmw == NULL) break;
  299.         if (event_xmw->type == SELECTION) {
  300.         /*
  301.          * We have entered a selection.
  302.          */
  303.         /* if (XPending(display) == 0) usleep(150000); */
  304.         if (XPending(display) != 0) {
  305.             XPeekEvent(display, &peek_event);
  306.             if(peek_event.type == LeaveNotify) {
  307.             break;
  308.             }
  309.         }              
  310.         cur_s = (XMSelect *)event_xmw;
  311.         /*
  312.          * If the pane we are in is active and the
  313.          * selection entered is active then activate
  314.          * the selection.
  315.          */
  316.         if (cur_p->active && cur_s->active > 0) {
  317.             cur_s->activated = 1;
  318.             _XMRefreshSelection(display, menu, cur_s);
  319.         }
  320.         }
  321.         else {
  322.         /*
  323.          * We have entered a pane.
  324.          */
  325.         /* if (XPending(display) == 0) usleep(150000); */
  326.         if (XPending(display) != 0) {
  327.             XPeekEvent(display, &peek_event);
  328.             if (peek_event.type == EnterNotify) break;
  329.         }
  330.         XQueryPointer(display,
  331.                   menu->parent,
  332.                   &root, &child,
  333.                   &root_x, &root_y,
  334.                   &win_x, &win_y,
  335.                   &mask);
  336.         event_xmp = (XMPane *)XLookUpAssoc(display,
  337.                            menu->assoc_tab,
  338.                            child);
  339.         if (event_xmp == NULL) break;
  340.         if (event_xmp == cur_p) break;
  341.         if (event_xmp->serial > cur_p->serial) forward = True;
  342.         else forward = False;
  343.         p_ptr = cur_p;
  344.         while (p_ptr != event_xmp) {
  345.             if (forward) p_ptr = p_ptr->next;
  346.             else p_ptr = p_ptr->prev;
  347.             XRaiseWindow(display, p_ptr->window);
  348.         }
  349.         if (cur_p->activated) {
  350.             cur_p->activated = False;
  351.             XSetWindowBackgroundPixmap(display,
  352.                            cur_p->window,
  353.                            menu->inact_pixmap);
  354.             _XMRefreshPane(display, menu, cur_p);
  355.         }
  356.         if (event_xmp->active) event_xmp->activated = True;
  357. #if 1
  358.         /*
  359.          * i suspect the we don't get an EXPOSE event when backing
  360.          * store is enabled; the menu windows content is probably
  361.          * not drawn in when it should be in that case.
  362.          * in that case, this is probably an ugly fix!
  363.          * i hope someone more familiar with this code would
  364.          * take it from here.  -- caveh@eng.sun.com.
  365.          */
  366.         XSetWindowBackground(display,
  367.                      event_xmp->window, 
  368.                      menu->bkgnd_color);
  369.         _XMRefreshPane(display, menu, event_xmp);
  370. #endif
  371.         cur_p = event_xmp;
  372.         }
  373.         break;
  374.     case LeaveNotify:
  375.         event_xmw = (XMWindow *)XLookUpAssoc(
  376.                          display,
  377.                          menu->assoc_tab,
  378.                          event.xcrossing.window
  379.                          );
  380.         if (event_xmw == NULL) break;
  381.         if(cur_s == NULL) break;
  382.         
  383.         /*
  384.          * If the current selection was activated then
  385.          * deactivate it.
  386.          */
  387.         if (cur_s->activated) {
  388.         cur_s->activated = False;
  389.         _XMRefreshSelection(display, menu, cur_s);
  390.         }
  391.         cur_s = NULL;
  392.         break;
  393.     
  394.     case ButtonPress:
  395.     case ButtonRelease:
  396.         *p_num = cur_p->serial;
  397.         /*
  398.          * Check to see if there is a current selection.
  399.          */
  400.         if (cur_s != NULL) {
  401.             /*
  402.              * Set the selection number to the current selection.
  403.              */
  404.             *s_num = cur_s->serial;
  405.             /*
  406.              * If the current selection was activated then
  407.              * we have a valid selection otherwise we have
  408.              * an inactive selection.
  409.              */
  410.             if (cur_s->activated) {
  411.             *data = cur_s->data;
  412.             ret_val = XM_SUCCESS;
  413.             }
  414.             else {
  415.             ret_val = XM_IA_SELECT;
  416.             }
  417.         }
  418.         else {
  419.             /*
  420.              * No selection was current.
  421.              */
  422.             ret_val = XM_NO_SELECT;
  423.         }
  424.         selection = True;
  425.         break;
  426.         default:
  427.         /*
  428.          * If AEQ mode is enabled then queue the event.
  429.          */
  430.         if (menu->aeq) {
  431.             feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
  432.             if (feq_tmp == NULL) {
  433.             _XMErrorCode = XME_CALLOC;
  434.             return(XM_FAILURE);
  435.             }
  436.             feq_tmp->event = event;
  437.             feq_tmp->next = feq;
  438.             feq = feq_tmp;
  439.         }
  440.         else if (_XMEventHandler) (*_XMEventHandler)(&event);
  441.     }
  442.     /*
  443.      * If a selection has been made, break out of the event loop.
  444.      */
  445.     if (selection == True) break;
  446.     }
  447.  
  448.     /*
  449.      * Unmap the menu.
  450.      */
  451.     for ( p_ptr = menu->p_list->next;
  452.      p_ptr != menu->p_list;
  453.      p_ptr = p_ptr->next) 
  454.       {
  455.       XUnmapWindow(display, p_ptr->window);
  456.       }
  457.  
  458.     /*
  459.      * Ungrab the mouse.
  460.      */
  461.     XUngrabPointer(display, CurrentTime);
  462.  
  463.     /* 
  464.      * Restore bits under where the menu was if we managed
  465.      * to save them and free the pixmap.
  466.      */
  467.  
  468.     /*
  469.      * If there is a current selection deactivate it.
  470.      */
  471.     if (cur_s != NULL) cur_s->activated = 0;
  472.  
  473.     /*
  474.      * Deactivate the current pane.
  475.      */
  476.     cur_p->activated = 0;
  477.     XSetWindowBackgroundPixmap(display, cur_p->window, menu->inact_pixmap);
  478.  
  479.     /*
  480.      * Synchronize the X buffers and the X event queue.
  481.      */
  482.     XSync(display, 0);
  483.     
  484.     /*
  485.      * Dispatch any events remaining on the queue.
  486.      */
  487.     while (QLength(display)) {
  488.     /*
  489.      * Fetch the next event.
  490.      */
  491.     XNextEvent(display, &event);
  492.  
  493.     /*
  494.      * Discard any events left on the queue that belong to XMenu.
  495.      * All others are held and then returned to the event queue.
  496.      */
  497.     switch (event.type) {
  498.         case Expose:
  499.         case EnterNotify:
  500.         case LeaveNotify:
  501.         case ButtonPress:
  502.         case ButtonRelease:
  503.         /*
  504.          * Does this event belong to one of XMenu's windows?
  505.          * If so, discard it and process the next event.
  506.          * If not fall through and treat it as a foreign event.
  507.          */
  508.         event_xmp = (XMPane *)XLookUpAssoc(
  509.                            display,
  510.                            menu->assoc_tab,
  511.                            event.xbutton.window
  512.                            );
  513.         if (event_xmp != NULL) continue;
  514.         default:
  515.         /*
  516.          * This is a foreign event.
  517.          * Queue it for later return to the X event queue.
  518.          */
  519.         feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
  520.         if (feq_tmp == NULL) {
  521.             _XMErrorCode = XME_CALLOC;
  522.             return(XM_FAILURE);
  523.         }
  524.         feq_tmp->event = event;
  525.         feq_tmp->next = feq;
  526.         feq = feq_tmp;
  527.         }
  528.     }
  529.     /*
  530.      * Return any foreign events that were queued to the X event queue.
  531.      */
  532.     while (feq != NULL) {
  533.     feq_tmp = feq;
  534.     XPutBackEvent(display, &feq_tmp->event);
  535.     feq = feq_tmp->next;
  536.     free((char *)feq_tmp);
  537.     }
  538.     
  539.     /*
  540.      * Return successfully.
  541.      */
  542.     _XMErrorCode = XME_NO_ERROR;
  543.     return(ret_val);
  544.  
  545. }
  546.